<!DOCTYPE html>
<html lang="zh">
<head>
    <title>多边形画板，绘制多边形</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

    <style>
        body {
            background: #eeeeee;
        }

        #controls {
            position: absolute;
            left: 25px;
            top: 25px;
        }

        #canvas {
            background: #ffffff;
            cursor: pointer;
            margin-left: 10px;
            margin-top: 10px;
            -webkit-box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
            -moz-box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
            box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
        }
    </style>
</head>

<body>
<canvas id='canvas' width='850' height='600'>
    Canvas not supported
</canvas>

<div id='controls'>
    <label for='strokeStyleSelect'> Stroke color:</label><select id='strokeStyleSelect'>
    <option value='red'>red</option>
    <option value='green'>green</option>
    <option value='blue'>blue</option>
    <option value='orange'>orange</option>
    <option value='cornflowerblue' selected>cornflowerblue</option>
    <option value='goldenrod'>goldenrod</option>
    <option value='navy'>navy</option>
    <option value='purple'>purple</option>
</select>

    <label for='fillStyleSelect'>Fill color: </label><select id='fillStyleSelect'>
    <option value='rgba(255,0,0,0.5)'>semi-transparent red</option>
    <option value='green'>green</option>
    <option value='rgba(0,0,255,0.5)'>semi-transparent blue</option>
    <option value='orange'>orange</option>
    <option value='rgba(100,140,230,0.5)'>semi-transparent cornflowerblue</option>
    <option value='goldenrod' selected>goldenrod</option>
    <option value='navy'>navy</option>
    <option value='purple'>purple</option>
</select>

    <label for='sidesSelect'>Sides: </label><select id='sidesSelect'>
    <option value=3 selected>3</option>
    <option value=4>4</option>
    <option value=5>5</option>
    <option value=6>6</option>
    <option value=7>7</option>
    <option value=8>8</option>
    <option value=9>9</option>
</select>

    <label for='startAngleSelect'>Start angle: </label><select id='startAngleSelect'>
    <option value=0 selected>0</option>
    <option value=22.5>22.5</option>
    <option value=45>45</option>
    <option value=67.5>67.5</option>
    <option value=90>90</option>
</select>

    <label for='fillCheckbox'>Fill</label><input id='fillCheckbox' type='checkbox' checked/>
    <input id='eraseAllButton' type='button' value='Erase all'/>
</div>
</body>
<script>
    'use strict';

    var canvas = document.getElementById('canvas'),
        context = canvas.getContext('2d'),
        eraseAllButton = document.getElementById('eraseAllButton'),
        strokeStyleSelect = document.getElementById('strokeStyleSelect'),
        startAngleSelect = document.getElementById('startAngleSelect'),

        fillStyleSelect = document.getElementById('fillStyleSelect'),
        fillCheckbox = document.getElementById('fillCheckbox'),

        sidesSelect = document.getElementById('sidesSelect'),

        drawingSurfaceImageData,

        mousedown = {},
        rubberbandRect = {},
        dragging = false,

        sides = 8,
        startAngle = 0,

        guidewires = true,

        Point = function (x, y) {
            this.x = x;
            this.y = y;
        };


    // Functions.....................................................

    function drawGrid(color, stepx, stepy) {
        context.save()

        context.strokeStyle = color;
        context.fillStyle = '#ffffff';
        context.lineWidth = 0.5;
        context.fillRect(0, 0, context.canvas.width, context.canvas.height);

        for (var i = stepx + 0.5; i < context.canvas.width; i += stepx) {
            context.beginPath();
            context.moveTo(i, 0);
            context.lineTo(i, context.canvas.height);
            context.stroke();
        }

        for (var i = stepy + 0.5; i < context.canvas.height; i += stepy) {
            context.beginPath();
            context.moveTo(0, i);
            context.lineTo(context.canvas.width, i);
            context.stroke();
        }

        context.restore();
    }

    function windowToCanvas(e) {
        var x = e.x || e.clientX,
            y = e.y || e.clientY,
            bbox = canvas.getBoundingClientRect();

        return { x: x - bbox.left * (canvas.width  / bbox.width),
            y: y - bbox.top  * (canvas.height / bbox.height)
        };
    }

    // save和restore的服务区域

    function saveDrawingSurface() {
        drawingSurfaceImageData = context.getImageData(0, 0,
            canvas.width,
            canvas.height);
    }

    function restoreDrawingSurface() {
        context.putImageData(drawingSurfaceImageData, 0, 0);
    }

    // Rubberbands...................................................

    function updateRubberbandRectangle(loc) {
        rubberbandRect.width = Math.abs(loc.x - mousedown.x);
        rubberbandRect.height = Math.abs(loc.y - mousedown.y);

        if (loc.x > mousedown.x) rubberbandRect.left = mousedown.x;
        else                     rubberbandRect.left = loc.x;

        if (loc.y > mousedown.y) rubberbandRect.top = mousedown.y;
        else                     rubberbandRect.top = loc.y;
    }

    function getPolygonPoints(centerX, centerY, radius, sides, startAngle) {
        var points = [],
            angle = startAngle || 0;

        for (var i=0; i < sides; ++i) {
            points.push(new Point(centerX + radius * Math.sin(angle),
                centerY - radius * Math.cos(angle)));
            angle += 2*Math.PI/sides;
        }

        return points;
    }

    function createPolygonPath(centerX, centerY, radius, sides, startAngle) {
        var points = getPolygonPoints(centerX, centerY, radius, sides, startAngle);

        context.beginPath();

        context.moveTo(points[0].x, points[0].y);

        for (var i=1; i < sides; ++i) {
            context.lineTo(points[i].x, points[i].y);
        }

        context.closePath();
    }

    function drawRubberbandShape(loc, sides, startAngle) {
        createPolygonPath(mousedown.x, mousedown.y,
            rubberbandRect.width,
            parseInt(sidesSelect.value),
            (Math.PI / 180) * parseInt(startAngleSelect.value));
        context.stroke();

        if (fillCheckbox.checked) {
            context.fill();
        }
    }

    function updateRubberband(loc, sides, startAngle) {
        updateRubberbandRectangle(loc);
        drawRubberbandShape(loc, sides, startAngle);
    }

    // Guidewires....................................................

    function drawHorizontalLine (y) {
        context.beginPath();
        context.moveTo(0,y+0.5);
        context.lineTo(context.canvas.width,y+0.5);
        context.stroke();
    }

    function drawVerticalLine (x) {
        context.beginPath();
        context.moveTo(x+0.5,0);
        context.lineTo(x+0.5,context.canvas.height);
        context.stroke();
    }

    function drawGuidewires(x, y) {
        context.save();
        context.strokeStyle = 'rgba(0,0,230,0.4)';
        context.lineWidth = 0.5;
        drawVerticalLine(x);
        drawHorizontalLine(y);
        context.restore();
    }

    // Event handlers................................................

    canvas.onmousedown = function (e) {
        var loc = windowToCanvas(e);

        saveDrawingSurface();

        e.preventDefault();

        saveDrawingSurface();
        mousedown.x = loc.x;
        mousedown.y = loc.y;
        dragging = true;
    };

    canvas.onmousemove = function (e) {
        var loc;

        if (dragging) {
            e.preventDefault();

            loc = windowToCanvas(e);
            restoreDrawingSurface();
            updateRubberband(loc, sides, startAngle);

            if (guidewires) {
                drawGuidewires(mousedown.x, mousedown.y);
            }
        }
    };

    canvas.onmouseup = function (e) {
        var loc = windowToCanvas(e);
        dragging = false;
        restoreDrawingSurface();
        updateRubberband(loc);
    };

    eraseAllButton.onclick = function (e) {
        context.clearRect(0, 0, canvas.width, canvas.height);
        drawGrid('lightgray', 10, 10);
        saveDrawingSurface();
    };

    strokeStyleSelect.onchange = function (e) {
        context.strokeStyle = strokeStyleSelect.value;
    };

    fillStyleSelect.onchange = function (e) {
        context.fillStyle = fillStyleSelect.value;
    };

    // 初始化

    context.strokeStyle = strokeStyleSelect.value;
    context.fillStyle = fillStyleSelect.value;
    drawGrid('lightgray', 10, 10);
</script>
</html>